package com.eagle.security.handler;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserCache;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.core.userdetails.cache.NullUserCache;
import org.springframework.security.provisioning.JdbcUserDetailsManager;
import org.springframework.transaction.annotation.Transactional;

import com.eagle.security.dao.UserGroupManager;
import com.eagle.security.entries.OverdueTypeConstant;
import com.eagle.security.entries.User;
import com.eagle.security.entries.UserExtendInfo;
import com.eagle.security.util.JsonUtils;
import com.eagle.security.util.SecurityUtils;

@Deprecated
public class UserDetailsServiceHandler extends JdbcUserDetailsManager  implements UserGroupManager{
    public static final String DEF_USERS_BY_USERNAME_QUERY = "select username,password,enabled,nick_name "//,extend_info,create_date,start_use_time,end_use_time,remark,overdue_type
        + "from users " + "where username = ?"; 

    public static final String DEF_ALL_USERS_QUERY = "select username,password,enabled,nick_name " //,create_date,start_use_time,end_use_time,remark,overdue_type
        + "from users ";

    public static final String DEF_USER_TOTAL_QUERY = "select count(username)  from users ";

    public static final String DEF_UPATE_USERS_NICK_NAME_SQL = "update users set nick_name=?, enabled=?  where username = ?";//, create_date=?,start_use_time=?,end_use_time=?,remark=?,overdue_type=?

    public static final String DEF_UPATE_USERS_NICK_NAME_SQL2 = "update users set nick_name=?,enabled=?  where username = ?"; //,start_use_time=?,end_use_time=?,remark=?,overdue_type=?

    public static final String DEF_UPATE_USERS_EXTEND_INFO_SQL = "update users set extend_info=? where username = ?";

    public static final String DEF_GROUP_BY_USERNAME_QUERY = "select g.id, g.group_name " + "from groups g, group_members gm "
        + "where gm.username = ? " + "and g.id = gm.group_id";

    public static final String DEF_FIND_GROUP_COUNT_SQL = "select count(id) from groups where group_name = ?";

    private String extraRoleNames = "";

    private UserCache userCache = new NullUserCache();

    {
        setUsersByUsernameQuery(DEF_USERS_BY_USERNAME_QUERY);
        setEnableGroups(true);
    }

    public void setUserCache(UserCache userCache) {
        super.setUserCache(userCache);
        this.userCache = userCache;
    }

    public String getExtraRoleNames() {
        return extraRoleNames;
    }

    public void setExtraRoleNames(String extraRoleNames) {
        this.extraRoleNames = extraRoleNames;
    }

    /**
     * 创建新用户
     */
    @Override
    public void createUser(final UserDetails user) {
        super.createUser(user);
        if (user instanceof User) {
            updateUser(user);
        }
    }

    /**
     * 用户名是否存在
     */
    @Override
    public boolean userExists(String username) {
        return super.userExists(username);
    }

    /**
     * 删除用户
     */
    @Override
    public void deleteUser(String username) {
        super.deleteUser(username);
    }

    /**
     * 修改用户信息
     */
    @Override
    public void updateUser(final UserDetails user) {
        if (user instanceof User) {
            getJdbcTemplate().update(DEF_UPATE_USERS_NICK_NAME_SQL, new PreparedStatementSetter() {
                public void setValues(PreparedStatement ps) throws SQLException {
                    User u = (User) user;
                    ps.setString(1, u.getNickname());
                    ps.setBoolean(2, user.isEnabled());
//                    ps.setTimestamp(3, u.getCreateDate());
//                    ps.setTimestamp(4, u.getStartUseTime());
//                    ps.setTimestamp(5, u.getEndUseTime());
//                    ps.setString(6, u.getRemark());
//                    ps.setInt(7, u.getOverdueType());
                    ps.setString(3, u.getUsername());
                }
            });
        }

        List<GrantedAuthority> userAuthorities = loadUserAuthorities(user.getUsername());
        if (userAuthorities == null) {
            userAuthorities = new ArrayList<GrantedAuthority>(0);
        }
        super.updateUser(new User(user.getUsername(), encrypt(user.getPassword()), user.isEnabled(), user.isAccountNonExpired(), user
            .isCredentialsNonExpired(), user.isAccountNonLocked(), userAuthorities));
    }

    public boolean verifyUserPassword(String username, String password) {
        if (StringUtils.isNotBlank(username) && StringUtils.isNotEmpty(password)) {
            try {
                String realPassword = loadUserByUsername(username).getPassword();

                if (encrypt(password).equals(realPassword)) {
                    return true;
                }
            }
            catch (UsernameNotFoundException ignore) {
            }
        }

        return false;
    }

    /**
     * 当前登录用户修改密码
     * 
     * @param oldPassword
     * @param newPassword
     * @throws AuthenticationException
     */
    public boolean changePasswordByPassword(String oldPassword, String newPassword) throws AuthenticationException {
        String currentUsername = SecurityUtils.getCurrentUser().getUsername();
        String realOldPassword = loadUserByUsername(currentUsername).getPassword();

        if (oldPassword == null || !encrypt(oldPassword).equals(realOldPassword)) {
            return false;
        }

        super.changePassword(encrypt(oldPassword), encrypt(newPassword));

        return true;
    }

    public boolean changePassword(String newPassword) throws AuthenticationException {
        String currentUsername = SecurityUtils.getCurrentUser().getUsername();
        String oldPassword = loadUserByUsername(currentUsername).getPassword();

        super.changePassword(oldPassword, encrypt(newPassword));

        return true;
    }

    /**
     * 获取指定用户名的信息
     * 
     * @param username
     *            用户名
     * @return cn.antvision.spring.security.entries.User
     */
    public User findUser(String username) {
        try {
            return (User) loadUserByUsername(username);
        }
        catch (UsernameNotFoundException ignore) {
            return null;
        }
    }

    /**
     * 修改用户信息
     */
    @Override
    public void updateUser(String username, Map<String, Object> params) {
        if (params == null || params.isEmpty()) {
            throw new IllegalArgumentException("缺少参数!");
        }

        List<Object> args = new ArrayList<Object>(10);

        String sql = DEF_UPATE_USERS_NICK_NAME_SQL2;

        if (params.containsKey("nickname")) {
            args.add(params.get("nickname"));
        }
        else {
            sql = sql.replace("nick_name=?,", "");
        }

        if (params.containsKey("enabled")) {
            args.add(params.get("enabled"));
        }
        else {
            sql = sql.replace("enabled=?,", "");
        }

        if (params.containsKey("startUseTime")) {
            args.add(params.get("startUseTime"));
        }
        else {
            sql = sql.replace("start_use_time=?,", "");
        }

        if (params.containsKey("endUseTime")) {
            args.add(params.get("endUseTime"));
        }
        else {
            sql = sql.replace("end_use_time=?,", "");
        }

        if (params.containsKey("remark")) {
            args.add(params.get("remark"));
        }
        else {
            sql = sql.replace("remark=?,", "");
        }

        if (params.containsKey("overdue_type")) {
            args.add(params.get("overdue_type"));
        }
        else {
            sql = sql.replace("overdue_type=?", "").replace("?, ", "? ");
        }

        if (args.isEmpty()) {
            throw new IllegalArgumentException("参数不正确!");
        }

        args.add(username);

        getJdbcTemplate().update(sql, args.toArray());

        userCache.removeUserFromCache(username);
    }

    /**
     * 修改用户额外信息
     */
    public void updateUserExtendInfo(final String username, final UserExtendInfo extendInfo) {
        getJdbcTemplate().update(DEF_UPATE_USERS_EXTEND_INFO_SQL, new PreparedStatementSetter() {
            public void setValues(PreparedStatement ps) throws SQLException {
                ps.setString(1, JsonUtils.parse(extendInfo));
                ps.setString(2, username);
            }
        });

        // final User user = (User) loadUserByUsername(username);

        // super.updateUser(user);
        userCache.removeUserFromCache(username);
    }

    /**
     * 修改制定用户密码
     */
    public void changePasswordByUsername(final String username, final String newPassword) throws AuthenticationException {
        Thread t = new Thread(new Runnable() {
            public void run() {
                UserDetails user = loadUserByUsername(username);
                UsernamePasswordAuthenticationToken newAuthentication = new UsernamePasswordAuthenticationToken(user, user.getPassword(),
                    user.getAuthorities());
                newAuthentication.setDetails(user);

                SecurityContextHolder.getContext().setAuthentication(newAuthentication);

                try {
                    UserDetailsServiceHandler.super.changePassword(user.getPassword(), encrypt(newPassword));
                }
                finally {
                    SecurityContextHolder.clearContext();
                }
            }
        });
        t.start();

        try {
            t.join(20000L);
        }
        catch (InterruptedException e) {
            throw new AuthenticationServiceException(e.getMessage(), e);
        }
    }

    /**
     * 创建组
     * 
     * @param groupName
     */
    public void createGroup(String groupName) {
        List<GrantedAuthority> emptyRoles = Collections.emptyList();
        super.createGroup(groupName, emptyRoles);
    }

    /**
     * 组名是否存在
     * 
     * @param groupName
     * @return
     */
    public boolean groupExists(String groupName) {
        return getJdbcTemplate().queryForInt(DEF_FIND_GROUP_COUNT_SQL, groupName) > 0;
    }

    /**
     * 删除组
     */
    @Override
    public void deleteGroup(String groupName) {
        super.deleteGroup(groupName);
    }

    /**
     * 为用户添加用户组
     */
    @Override
    public void addUserToGroup(String username, String groupName) {
        List<String> userGroups = loadGroupsByUsername(username);

        if (userGroups == null || !userGroups.contains(groupName)) {
            super.addUserToGroup(username, groupName);
        }
    }

    /**
     * 重命名角色组名称
     */
    @Override
    public void renameGroup(String oldName, String newName) {
        super.renameGroup(oldName, newName);
    }

    /**
     * 将用户从指定角色组中移除
     */
    @Override
    public void removeUserFromGroup(String username, String groupName) {
        super.removeUserFromGroup(username, groupName);
    }

    /**
     * 设置用户所在的角色组
     * 
     * @param username
     * @param groupNames
     *            可为空
     */
    public void setUserGroups(String username, String... groupNames) {
        setUserGroups(username, toUnNullList(groupNames));
    }

    /**
     * 设置用户所在的角色组
     * 
     * @param username
     * @param groupNames
     *            可为空
     */
    public void setUserGroups(String username, Collection<String> groupNames) {
        Set<String> uniqueGroupNames = toUnNullSet(groupNames);
        List<String> oldGroupNames = loadGroupsByUsername(username);

        if (oldGroupNames != null) {
            for (String groupName : oldGroupNames) {
                if (uniqueGroupNames.contains(groupName)) {
                    uniqueGroupNames.remove(groupName);
                }
                else {
                    removeUserFromGroup(username, groupName);
                }
            }
        }

        for (String groupName : uniqueGroupNames) {
            addUserToGroup(username, groupName);
        }
    }

    /**
     * 设置角色组的权限
     * 
     * @param groupName
     * @param roles
     *            可为空
     */
    public void setGroupRoles(String groupName, String... roles) {
        setGroupRoles(groupName, toUnNullList(roles));
    }

    @Override
    public Collection<String> findGroupRoles(String groupName) {
        List<GrantedAuthority> groupAuthorities = findGroupAuthorities(groupName);
        if (groupAuthorities != null) {
            TreeSet<String> roles = new TreeSet<String>();
            for (GrantedAuthority a : groupAuthorities) {
                roles.add(a.getAuthority());
            }
            return roles;
        }

        return Collections.emptyList();
    }

    /**
     * 设置角色组的权限
     * 
     * @param groupName
     * @param roles
     *            可为空
     */
    public void setGroupRoles(String groupName, Collection<String> roles) {
        Set<String> uniqueRoles = toUnNullSet(roles);
        List<GrantedAuthority> groupAuthorities = findGroupAuthorities(groupName);

        if (groupAuthorities != null) {
            String role;
            for (GrantedAuthority a : groupAuthorities) {
                role = a.getAuthority();
                if (uniqueRoles.contains(role)) {
                    uniqueRoles.remove(role);
                }
                else {
                    removeGroupAuthority(groupName, a);
                }
            }
        }

        for (String role : uniqueRoles) {
            addGroupAuthority(groupName, new SimpleGrantedAuthority(role));
        }
    }

    /**
     * 查找用户所在的角色组
     * 
     * @param username
     * @return
     */
    public List<String> loadGroupsByUsername(String username) {
        return getJdbcTemplate().query(DEF_GROUP_BY_USERNAME_QUERY, new String[] { username }, new RowMapper<String>() {
            public String mapRow(ResultSet rs, int rowNum) throws SQLException {
                return rs.getString(2);
            }
        });
    }

    @Override
    public Collection<String> loadUserRolesByUsername(String username) {
        List<GrantedAuthority> userAuthorities = loadUserAuthorities(username);
        if (userAuthorities != null) {
            TreeSet<String> data = new TreeSet<String>();
            for (GrantedAuthority a : userAuthorities) {
                data.add(a.getAuthority());
            }
            return data;
        }

        return Collections.emptyList();
    }

    @Override
    protected void addCustomAuthorities(String username, List<GrantedAuthority> authorities) {
        if (authorities.isEmpty()) {
            authorities.add(new SimpleGrantedAuthority("ROLE_DEFLAUT_LOGIN"));
        }
    }

    @Override
    public Collection<String> loadGroupRolesByUsername(String username) {
        List<GrantedAuthority> groupAuthorities = loadGroupAuthorities(username);
        if (groupAuthorities != null) {
            TreeSet<String> data = new TreeSet<String>();
            for (GrantedAuthority a : groupAuthorities) {
                data.add(a.getAuthority());
            }
            return data;
        }

        return Collections.emptyList();
    }

    @Override
    public void setUserRoles(String username, String... roles) {
        setUserRoles(username, toUnNullList(roles));
    }

    @Override
    @Transactional
    public void setUserRoles(String username, Collection<String> roles) {
        roles = toUnNullSet(roles);

        getJdbcTemplate().update(DEF_DELETE_USER_AUTHORITIES_SQL, username);

        for (String role : roles) {
            getJdbcTemplate().update(DEF_INSERT_AUTHORITY_SQL, username, role);
        }

        userCache.removeUserFromCache(username);
    }

    private static Set<String> toUnNullSet(Collection<String> values) {
        if (values == null || values.isEmpty()) {
            return Collections.emptySet();
        }
        else {
            Set<String> data = new HashSet<String>(values);
            data.remove(null);
            return data;
        }
    }

    private static List<String> toUnNullList(String... values) {
        List<String> data = Collections.emptyList();
        if (values != null && values.length > 0) {
            data = new ArrayList<String>(values.length);
            for (String v : values) {
                data.add(v);
            }
        }

        return data;
    }

    //TODO
    @Override
    protected List<UserDetails> loadUsersByUsername(String username) {
        return getJdbcTemplate().query(getUsersByUsernameQuery(), new String[] { username }, new RowMapper<UserDetails>() {
            public UserDetails mapRow(ResultSet rs, int rowNum) throws SQLException {
                String username = rs.getString(1);
                String password = rs.getString(2);
                boolean enabled = rs.getBoolean(3);
                String nickname = rs.getString(4);
//                UserExtendInfo extendInfo = JsonUtils.convert(rs.getString(5), UserExtendInfo.class);
//                Timestamp createDate = rs.getTimestamp(6);
//                Timestamp startUseTime = rs.getTimestamp(7);
//                Timestamp endUseTime = rs.getTimestamp(8);
//                String remark = rs.getString(9);
//                int overdueType = rs.getInt(10);

                User user = new User(username, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES, username, nickname);

//                if (extendInfo != null) {
//                    user.setExtendInfo(extendInfo);
//                }
//
//                if (createDate != null) {
//                    user.setCreateDate(createDate);
//                }
//
//                user.setStartUseTime(startUseTime);
//                user.setEndUseTime(endUseTime);
//                user.setRemark(remark);
//                user.setOverdueType(overdueType);

                return user;
            }
        });
    }

    @Override
    public List<User> findUsers(int start, int end, String where, String orderBy, Object... values) {
        if (StringUtils.isBlank(orderBy)) {
            orderBy = " ORDER BY username";
        }

        if (StringUtils.isBlank(where)) {
            where = "";
        }

        if (values == null) {
            values = new String[0];
        }

        return getJdbcTemplate().query(getLimitString(DEF_ALL_USERS_QUERY + where + " " + orderBy, start, end), values,
            new RowMapper<User>() {
                public User mapRow(ResultSet rs, int rowNum) throws SQLException {
                    String username = rs.getString(1);
                    String password = rs.getString(2);
                    boolean enabled = rs.getBoolean(3);
                    String nickname = rs.getString(4);
//                    Timestamp createDate = rs.getTimestamp(5);
//                    Timestamp startUseTime = rs.getTimestamp(6);
//                    Timestamp endUseTime = rs.getTimestamp(7);
//                    String remark = rs.getString(8);
//                    int overdueType = rs.getInt(9);
//
                    User u = new User(username, password, enabled, true, true, true, AuthorityUtils.NO_AUTHORITIES, username, nickname);
//
//                    if (createDate != null) {
//                        u.setCreateDate(createDate);
//                    }
//
//                    u.setStartUseTime(startUseTime);
//                    u.setEndUseTime(endUseTime);
//                    u.setRemark(remark);
//                    u.setOverdueType(overdueType);

                    return u;
                }
            });
    }

    @Override
    public int getUserTotal() {
        return getJdbcTemplate().queryForInt(DEF_USER_TOTAL_QUERY);
    }

    @Override
    public List<String> findAllGroups() {
        return super.findAllGroups();
    }

    @Override
    public List<String> findUsersInGroup(String groupName) {
        return super.findUsersInGroup(groupName);
    }

    public boolean isUserInGroup(String username, String groupName) {
        List<String> userGroups = loadGroupsByUsername(username);
        return userGroups != null && userGroups.contains(groupName);
    }

    @Override
    protected UserDetails createUserDetails(String username, UserDetails userFromUserQuery, List<GrantedAuthority> combinedAuthorities) {
        String returnUsername = userFromUserQuery.getUsername();

        if (!isUsernameBasedPrimaryKey()) {
            returnUsername = username;
        }

        String id = returnUsername;
        String nickname = returnUsername;
        UserExtendInfo extendInfo = null;
        Timestamp createDate = null;
        Timestamp startUseTime = null;
        Timestamp endUseTime = null;
        String remark = null;
        int overdueType = OverdueTypeConstant.DELAY;

        if (userFromUserQuery instanceof User) {
            User u = (User) userFromUserQuery;
            nickname = u.getNickname();
            extendInfo = u.getExtendInfo();
            createDate = u.getCreateDate();
            startUseTime = u.getStartUseTime();
            endUseTime = u.getEndUseTime();
            remark = u.getRemark();
            overdueType = u.getOverdueType();
        }

        User user = new User(returnUsername, userFromUserQuery.getPassword(), userFromUserQuery.isEnabled(), true, true, true,
            combinedAuthorities, id, nickname);

        if (extendInfo != null) {
            user.setExtendInfo(extendInfo);
        }

        if (createDate != null) {
            user.setCreateDate(createDate);
        }

        user.setStartUseTime(startUseTime);
        user.setEndUseTime(endUseTime);
        user.setRemark(remark);
        user.setOverdueType(overdueType);

        return user;
    }

    @Override
    protected List<GrantedAuthority> loadGroupAuthorities(String username) {
        
//        return getJdbcTemplate().query(groupAuthoritiesByUsernameQuery, new String[] {username}, new RowMapper<GrantedAuthority>() {
//            public GrantedAuthority mapRow(ResultSet rs, int rowNum) throws SQLException {
//                 String roleName = getRolePrefix() + rs.getString(3);
//
//                return new SimpleGrantedAuthority(roleName);
//            }
//        });
        
        List<GrantedAuthority> data = super.loadGroupAuthorities(username);
        if (data != null && StringUtils.isNotBlank(getExtraRoleNames())) {
            for (String name : getExtraRoleNames().split("\\s*,\\s*")) {
                name = StringUtils.trim(name);
                if (StringUtils.isNotEmpty(name)) {
                    data.add(new SimpleGrantedAuthority(getRolePrefix() + name));
                }
            }
        }
        return data;
    }

    public String encrypt(String password) {
        return DigestUtils.md5Hex(password);
    }

    private boolean isOracle;

    public boolean isOracle() {
        return isOracle;
    }

    public void setOracle(boolean isOracle) {
        this.isOracle = isOracle;
    }

    public String getLimitString(String sql, int start, int end) {
        return getLimitString(sql, true, start, end);
    }

    public String getLimitString(String sql, boolean hasOffset, int start, int end) {
        if (isOracle()) {
            StringBuffer pagingSelect = new StringBuffer(sql.length() + 100);
            if (hasOffset) {
                pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
            }
            else {
                pagingSelect.append("select * from ( ");
            }
            pagingSelect.append(sql);
            if (hasOffset) {
                pagingSelect.append(" ) row_ where rownum <= ").append(end).append(") where rownum_ > ").append(start);
            }
            else {
                pagingSelect.append(" ) where rownum <=").append(end);
            }

            return pagingSelect.toString();
        }
        else {
            return new StringBuffer(sql.length() + 20).append(sql)
                .append(hasOffset ? " limit " + start + "," + (end - start) : " limit " + (end - start)).toString();
        }
    }

}
